package com.zxmzy.scaffold.http

import android.content.Context
import com.zxmzy.scaffold.http.cookie.CookieJar
import com.zxmzy.scaffold.http.interceptor.HeaderInterceptor
import com.zxmzy.scaffold.http.interceptor.LoggingInterceptor
import com.zxmzy.scaffold.http.ssl.XTrustManager
import okhttp3.Cache
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import java.security.SecureRandom
import java.util.concurrent.TimeUnit
import javax.net.ssl.HostnameVerifier
import javax.net.ssl.SSLContext

/**
 * @author zhangxiaoming
 * @date 2023/8/25
 * @description
 */
class RequestManager {
    internal lateinit var appContext: Context
    private lateinit var retrofit: Retrofit
    private lateinit var baseUrl: String
    private lateinit var headers: HeaderInterceptor
    private var retrofitDSL: (RetrofitDSL.() -> Unit)? = null
    private var isDebug: Boolean = false

    companion object {
        private const val TIMEOUT_TIME: Long = 10
        private val instance: RequestManager by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
            RequestManager()
        }

        fun get(): RequestManager = instance
    }

    @JvmOverloads
    fun init(
        context: Context,
        baseUrl: String,
        isDebug: Boolean = false,
        retrofitDSL: (RetrofitDSL.() -> Unit)? = null
    ) {
        this.appContext = context.applicationContext
        this.baseUrl = baseUrl
        this.isDebug = isDebug
        this.retrofitDSL = retrofitDSL
        this.headers = HeaderInterceptor()
        init(retrofitDSL)
    }

    private fun init(retrofitDSL: (RetrofitDSL.() -> Unit)? = null) {
        initRetrofit(getOkHttp(), retrofitDSL)
    }

    private fun getOkHttp(): OkHttpClient.Builder {
        val builder: OkHttpClient.Builder = OkHttpClient.Builder()
        if (isDebug) {
            builder.addNetworkInterceptor(LoggingInterceptor())
        }
        try {
            val sslContext = SSLContext.getInstance("SSL")
            sslContext.init(null, arrayOf(XTrustManager()), SecureRandom())
            val sslSocketFactory = sslContext.socketFactory
            builder.sslSocketFactory(sslSocketFactory, XTrustManager())
            builder.hostnameVerifier(HostnameVerifier { _, _ ->
                true
            })

        } catch (e: Exception) {
            throw RuntimeException(e)
        }

        return builder.cache(Cache(appContext.cacheDir, 10 * 1024 * 1024L))
            .addInterceptor(headers)
            .connectTimeout(TIMEOUT_TIME, TimeUnit.SECONDS)
            .readTimeout(TIMEOUT_TIME, TimeUnit.SECONDS)
            .writeTimeout(TIMEOUT_TIME, TimeUnit.SECONDS)
            .cookieJar(CookieJar.instance)
    }

    private fun initRetrofit(
        okHttpBuilder: OkHttpClient.Builder,
        retrofitDSL: (RetrofitDSL.() -> Unit)? = null
    ) {
        val dsl = if (retrofitDSL != null) RetrofitDSL().apply(retrofitDSL) else null
        val finalOkHttpBuilder = dsl?.buildOkHttp?.invoke(okHttpBuilder) ?: okHttpBuilder
        val retrofitBuilder = Retrofit.Builder()
            .baseUrl(baseUrl)
            .addConverterFactory(GsonConverterFactory.create())
            .client(finalOkHttpBuilder.build())
        val finalRetrofitBuilder = dsl?.buildRetrofit?.invoke(retrofitBuilder) ?: retrofitBuilder
        this.retrofit = finalRetrofitBuilder.build()
    }

    fun <Service> apiService(service: Class<Service>): Service {
        return retrofit.create(service)
    }

    fun resetBaseUrl(newValue: String): Boolean {
        val isOk =
            newValue.isNotBlank() && (baseUrl.startsWith("http://") || baseUrl.startsWith("https://"))
        check(isOk) { "baseUrl is illegal: $baseUrl" }
        val isChanged = isOk && baseUrl != newValue
        if (isChanged) {
            init(appContext, newValue, isDebug, retrofitDSL)
        }

        return isChanged
    }

    fun putHead(key: String, value: String): HeaderInterceptor {
        headers.put(key, value)
        return headers
    }
}

class RetrofitDSL {
    internal var buildOkHttp: ((OkHttpClient.Builder) -> OkHttpClient.Builder)? = null
    internal var buildRetrofit: ((Retrofit.Builder) -> Retrofit.Builder)? = null
    infix fun okhttp(builder: ((OkHttpClient.Builder) -> OkHttpClient.Builder)?) {
        this.buildOkHttp = builder
    }

    infix fun retrofit(builder: ((Retrofit.Builder) -> Retrofit.Builder)?) {
        this.buildRetrofit = builder
    }
}